package com.zengqingfa.springboot.redis.delay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Set;
import java.util.UUID;

/**
 * @author zengqingfa
 * @className RedisDelayQueue
 * @description
 * @create 2022/9/24 17:03
 */
@Slf4j
public abstract class AbstractRedisDelayQueue<T> {

    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 获取到子类的队列的名字
     *
     * @return
     */
    protected abstract String getDelayTaskName();

    /**
     * 父类调用子类方法：获取到队列名称
     *
     * @return
     */
    public String getName() {
        return getDelayTaskName();
    }


    /**
     * 设置延迟消息
     */
    public void setDelayTasks(T msg, long delayTime) {
        DelayTask<T> delayTask = new DelayTask<>();
        delayTask.setId(UUID.randomUUID().toString());
        delayTask.setMsg(msg);
        Boolean addResult = redisTemplate.opsForZSet().add(getName(), JSONObject.toJSONString(delayTask), System.currentTimeMillis() + delayTime);
        if (addResult) {
            log.info("添加任务成功！" + JSONObject.toJSONString(delayTask) + "当前时间为" + LocalDateTime.now());
        }
    }

    /**
     * 执行业务逻辑,延迟到子类执行
     */
    public abstract void execute(DelayTask delayTask);

    /**
     * 监听延迟消息
     */
    public void listenDelayLoop() {
        while (true) {
            // 获取一个到点的消息
            /**
             * 	/**
             * 	 * Get elements in range from {@code start} to {@code end} where score is between {@code min} and {@code max} from
             * 	 * sorted set.
             * 	 *
             * 	 * @param key must not be {@literal null}.
             * 	 * @param min
             * 	 * @param max
             * 	 * @param offset
             * 	 * @param count
             * 	 * @return {@literal null} when used in pipeline / transaction.
             * 	 * @see <a href="https://redis.io/commands/zrangebyscore">Redis Documentation: ZRANGEBYSCORE</a>
             */
            Set<String> set = redisTemplate.opsForZSet().rangeByScore(getName(), 0, System.currentTimeMillis(), 0, 1);

            // 如果没有，就等等
            if (set.isEmpty()) {
                try {
                    log.info("{}没有监听到消息", getName());
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    log.error("监听异常：{}", e);
                }
                // 继续执行
                continue;
            }
            log.info("监听到消息：{}", JSON.toJSONString(set));
            // 获取具体消息的key
            String it = set.iterator().next();
            // 删除对应的元素
            if (redisTemplate.opsForZSet().remove(getName(), it) > 0) {
                // 拿到任务
                DelayTask delayTask = JSONObject.parseObject(it, DelayTask.class);
                // 后续处理
                log.info("消息到期" + delayTask.getMsg().toString() + ",时间为" + LocalDateTime.now());
                //TODO 业务处理
                execute(delayTask);
            }
        }
    }
}
